查看原文
其他

X86 汇编:真的是“恐怖”的存在吗?

StrokMitream 看雪学院 2019-05-26

Assembly Language(汇编语言),对于哪些从未深入了解其中的人来说,可谓是高深莫测之极。由于我们的计算机是以汇编语言为基础开发出来的,了解这些内容将会极大地有助于我们弄清CPU的工作原理。


 

 

X86 计算机对 IBM PC 原生的完全后向兼容-从系统顶层到加电启动甚至底层硬件电路,实在是令人印象深刻。 这意味着几乎所有的 PC 都能运行1983年以后开发的软件;不过,需要为此付出的代价也是非常之高的。X86 架构由于它的 edge case 和复杂性而“名声大振”。

 

要明白一台现代 X86 计算机的启动过程,我们需要了解 X86 架构的发展历史,启动过程在 99%的情况下是由 bootloader 处理的。

 



在我们的 demo 中将专注于16位“实模式”-DOS系统和其他变种操作系统的运行环境。在该模式下,CPU并不支持现代操作系统所具有的各种安全保护措施。运行于其上的程序可以访问系统各个角落甚至修改系统。这使得 DOS 病毒大量泛滥(尽管这些病毒大都不具破坏性,但却很令人厌烦)。

 

在实模式下,与硬件交互的模型也极其简单。PC 通过外部丰富的软中断与各式各样的硬件交互。

 

这让使得最初的阶段的系统开发工作容易起来:因为开发者只需知道少许软中断即可完成绝大多数 I/O 操作。

 

了解这些之后,我们开始我们的第一个 demo:

[BITS 16]  ;表明为 16 位汇编代码
[ORG 0x7C00];起始地址,告知汇编器将代码加载到内存的位置

mov ah, 0x0A ; 设置 BIOS 调用类型
mov al, 66   ; 待打印的字母
mov cx, 1    ; 打印的次数
mov bh, 0    ; 页号

int 0x10     ; 调用 BIOS 打印字母

hlt          ; 暂停,处理器不执行任何操作

TIMES 510 - ($ - $$) db 0    ;用 0 填充该扇区的剩余空间
DW 0xAA55          ; 扇区结束标识


上面的代码什么意思呢? 其中mov 指令是用来把数据从一个位置传送到另一个地方。在此,我们只是用来在寄存器间传送数据,并未涉及内存空
间。
int 指令指代一系列软中断,在这段代码中是 16号中断。

 

接下来,既然我们已经执行完我们的任务,那么调用hlt指令停止 CPU 运行。

 

到现阶段,是不是有点疑惑,我们是怎么就执行到这一步了呢?计算机刚才又是如何执行这些指令的?

 

实际上,系统启动的第一阶段是启动 BIOS - 用于配置和检测系统硬件。同时, BIOS 输出系统信息给接下来启动的操作系统程序,以便于接下来的硬件检测工作。

 

假定 BIOS 知道它是从何种设备上启动,那么,它首先会加载该设备
的第一个扇区512字节到内存中的指定位置,然后跳转到此处将控制权移交给下一个启动程序。

 

 

在上面的 demo 中,程序仅读取了512字节的有效载荷。如果我们需要执行的程序超过512字节大小,怎么办呢?

 

有幸的是,BIOS 可以通过调用 13号 int 中断,来访问系统中的磁盘驱动器。

[BITS 16]
org 0x7C00
start:
       ; This section of code is added based on Michael Petch's bootloader tips
       xor ax,ax      ; 为 DS 置 0 准备
       mov ds,ax  
       mov bx,0x8000  ; 栈段可以是可用内存的任意一段
       mov ss,bx      ; 栈顶位于 0x80000.
       mov sp,ax      ; 设置 SP=0 ,使栈底位于 0x90000
       cld            ; 设置 DF 位为正向
       mov ah, 0x02
       mov al, 1  
       mov ch, 0  
       mov cl, 2  
       mov dh, 0  
       mov bx, new
       mov es, bx
       xor bx, bx
       int 0x13
       jmp new:0
data:
       new equ 0x0500
times   510-($-$$) db 0
dw      0xaa55            
sect2:
       mov ax, cs
       mov ds, ax    ; 设置 CS=DS. CS=0x0500, 因此 DS=0x500
                     ; 如果变量已经在代码中设置,则要求
                     ; 正确地引用其内存地址
       mov ax, 0xB800
       mov es, ax
       mov byte [es:420], 'H'
       mov byte [es:421], 0x48
       mov byte [es:422], 'E'
       mov byte [es:423], 0x68
       mov byte [es:424], 'L'
       mov byte [es:425], 0x28
       mov byte [es:426], 'L'
       mov byte [es:427], 0x38
       mov byte [es:428], 'O'
       mov byte [es:429], 0x18
       mov byte [es:430], '!'
       mov byte [es:431], 0x58
       hlt


上述代码,加载虚拟磁盘的下一扇区 ,然后跳转到该处。13号 int 中断 API十分简洁,透露出计算机存储器工作的底层细节(尽管flsh存储器广泛使用,众多的数值依旧保留以保持兼容)。

 

 

BIOS 提供的服务极其的丰富,这有一个非常棒的参考资料:


  • http://www.ctyme.com/intr/rb-0608.htm

 



磁盘 - 磁盘扇区写入参数定义


  • AH = 03h

  • AL = 写入的扇区数 (必须非零)

  • CH = 柱面数低8位

  • CL = 1-63扇区号 (bits 0-5)

  • 柱面数的高2位 (6-7位, 只用于硬盘hard disk only)
    DH = 磁头数

  • DL = 驱动器编号 (磁盘为硬盘驱动器时第7位置位)

  • ES:BX -> 数据缓冲区

 


返回值:


  • CF 写入错误

  • 时置位

  • CF 写入成功时清除置位

  • AH = 磁盘状态

  • AL = 传输的扇区数

(仅当某些BIOS的 CF 置位时有效 )




- End -



看雪ID:StrokMitream                           

https://bbs.pediy.com/user-747320.htm



本文由看雪翻译小组 StrokMitream 编译

来源Ben Cox @blog.benjojo.co.uk

转载请注明来自看雪社区


好书推荐:

立即购买!



热门技术文章推荐:




公众号ID:ikanxue
官方微博:看雪安全

商务合作:wsc@kanxue.com


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存